# -*- coding: utf-8 -*-
# created on 2018/05/15

import collections
from mathsolver.functions.base import *
from mathsolver.functions.shulie.Fundamental import *
from sympy import Tuple
from mathsolver.functions.mathematica.mathematicaSolve import MathematicaSimplify, MathematicaSolve


def sl_solve_eqs(eqs, aname, b, j):  # 方程组, 变量1, 变量2, 变量3
    """
    # 解数列中的方程
    :param eqs:
    :param aname:
    :param b:
    :param j:
    :return:
    """
    type_list = [0, 0, 0]  # a_(1), b, j(固定变量): TongXiang = b * n + j
    type_xiangs = [aname(1), b, j]
    for eq in eqs:
        expr = eq.expand().simplify()
        m = sl_xiangs(expr)
        if m:
            type_list[0] = 1
            for item in m:
                if item == str(aname(1)):
                    type_xiangs[0] = sympify(item)
            symbols = list(expr.free_symbols)
            if symbols:
                for item in symbols:
                    if item == j:
                        type_list[2] = 1
                        type_xiangs[2] = item
                    else:
                        type_list[1] = 1
                        type_xiangs[1] = item
        else:
            symbols = list(expr.free_symbols)
            if symbols:
                for item in symbols:
                    if item == j:
                        type_list[2] = 1
                        type_xiangs[2] = item
                    else:
                        type_list[1] = 1
                        type_xiangs[1] = item
    solutions = collections.defaultdict(list)
    if sum(type_list) == 1:
        if type_list[0] == 1:
            print(1)
        elif type_list[1] == 1:
            print(1)
        elif type_list[2] == 1:
            sympy_answer = solve(eqs, type_xiangs[2], dict=True)
            if sympy_answer and isinstance(sympy_answer, list):
                key_j = type_xiangs[2]
                values_j = []
                answer = {}
                for ans in sympy_answer:
                    j1 = ans[key_j]
                    values_j.append(j1)
                answer[key_j] = values_j
                known_sym, known_solve = zip(*answer.items())
                for known in zip(*known_solve):
                    solutions[known_sym].append(tuple(known))
    elif sum(type_list) == 2:
        if type_list[0] == 0:
            sympy_answer = solve(eqs, type_xiangs[1], type_xiangs[2], dict=True)
            if sympy_answer and isinstance(sympy_answer, list):
                key_b = type_xiangs[1]
                values_b = []
                key_j = type_xiangs[2]
                values_j = []
                answer = {}
                for ans in sympy_answer:
                    keys = ans.keys()
                    if len(keys) == 1:
                        print(1)
                    elif len(keys) == 2:
                        b1 = ans[key_b]
                        j1 = ans[key_j]
                        values_b.append(b1)
                        values_j.append(j1)
                    answer[key_b] = values_b
                    answer[key_j] = values_j
                    known_sym, known_solve = zip(*answer.items())
                    for known in zip(*known_solve):
                        solutions[known_sym].append(tuple(known))
        elif type_list[1] == 0:
            sympy_answer = solve(eqs, type_xiangs[0], type_xiangs[2], dict=True)
            if sympy_answer and isinstance(sympy_answer, list):
                key_a1 = type_xiangs[0]
                values_a1 = []
                key_j = type_xiangs[2]
                values_j = []
                answer = {}
                for ans in sympy_answer:
                    keys = ans.keys()
                    if len(keys) == 1:
                        print(1)
                    elif len(keys) == 2:
                        a11 = ans[key_a1]
                        j1 = ans[key_j]
                        values_a1.append(a11)
                        values_j.append(j1)
                    answer[key_a1] = values_a1
                    answer[key_j] = values_j
                    known_sym, known_solve = zip(*answer.items())
                    for known in zip(*known_solve):
                        solutions[known_sym].append(tuple(known))
        elif type_list[2] == 0:
            print(1)
    elif sum(type_list) == 3:
        sympy_answer = solve(eqs, type_xiangs[0], type_xiangs[1], type_xiangs[2], dict=True)
        if sympy_answer and isinstance(sympy_answer, list):
            key_a1 = type_xiangs[0]
            values_a1 = []
            key_b = type_xiangs[1]
            values_b = []
            key_j = type_xiangs[2]
            values_j = []
            answer = {}
            for ans in sympy_answer:
                keys = ans.keys()
                if len(keys) == 1:
                    print(1)
                elif len(keys) == 2:
                    print(1)
                elif len(keys) == 3:
                    a11 = ans[key_a1]
                    b1 = ans[key_b]
                    j1 = ans[key_j]
                    values_a1.append(a11)
                    values_b.append(b1)
                    values_j.append(j1)
            answer[key_a1] = values_a1
            answer[key_b] = values_b
            answer[key_j] = values_j
            known_sym, known_solve = zip(*answer.items())
            for known in zip(*known_solve):
                solutions[known_sym].append(tuple(known))
    return type_list, type_xiangs, solutions


def sl_search_multiple(known, poly):
    symbols = poly.free_symbols
    symbols = list(symbols)
    poly_xiangs = sl_xiangs(poly)
    if poly_xiangs:
        for item in poly_xiangs:
            symbols.append(sympify(item))
    symbols = set(symbols)
    answer = []
    for name in known:
        if isinstance(name, Tuple):
            new_name = set(name)
            if new_name & symbols <= new_name:
                values = known[name]
                for value in values:
                    type_flag = []
                    for sym, v in zip(name, value):
                        if isinstance(v, Interval):
                            type_flag.append(1)
                        else:
                            type_flag.append(0)
                    if sum(type_flag) == 0:
                        symbol_value = dict([(sym, v) for sym, v in zip(name, value)])
                        answer.append(symbol_value)
                    else:
                        for sym, v in zip(name, value):
                            if not isinstance(v, Interval):
                                answer.append({sym: v})
        else:
            if name in symbols:
                values = list(known[name])
                for value in values:
                    symbol_value = {name: value}
                    answer.append(symbol_value)
    return answer


def sl_search_known(known, name):
    if name == "NN" and name not in known:
        name = "N"

    if name == "N" and name not in known:
        name = "NN"

    if name in known:
        return known[name]
    else:
        for key, value in known.items():
            if isinstance(key, tuple) and name in key:
                index = key.index(name)
                return FiniteSet(*[v[index] for v in value])
            elif str(key) == str(name):
                return value
            elif str(key).find(str(name)) >= 0:  # search a_S_类
                import re
                p = r'[A-Z]_'
                m = re.findall(p, str(key))
                if m:
                    return value
        return None


def sl_seqs_other_seqs(known, seqs):
    new_seqs = []
    new_other_seqs = []
    for eq in seqs:
        if len(eq) == 2:
            expr = (sympify(eq[0]) - sympify(eq[1])).expand().simplify()
        else:
            expr = (sympify(eq[0]) - sympify(eq[2])).expand().simplify()
        expr_xiangs = sl_shulie_name(expr)
        flag = []
        if not expr_xiangs:
            flag.append(0)
        else:
            for xiang in expr_xiangs:
                sl = sl_search_known(known, str(xiang))
                if sl:
                    flag.append(0)
                else:
                    flag.append(1)
        if sum(flag) >= 1:
            new_other_seqs.append(eq)
        else:
            new_seqs.append(eq)
    return new_seqs, new_other_seqs


def sl_shulies_flag(known, seqs):
    shulie_flag = []
    sls = []
    for eq in seqs:
        if len(eq) == 2:
            expr = (sympify(eq[0]) - sympify(eq[1])).expand().simplify()
        else:
            expr = (sympify(eq[0]) - sympify(eq[2])).expand().simplify()
        xiangs = sl_xiangs(expr)
        if xiangs:
            for xiang in xiangs:
                new_xiang = sympify(xiang)
                new_xiang_name = new_xiang.func
                sl = sl_search_known(known, str(new_xiang_name))
                sls.append(sl)
                sl_type = sl.type
                if sl_type == "DengCha" or sl_type == "DengBi":
                    shulie_flag.append(0)
                else:
                    shulie_flag.append(1)
    sls = set(sls)
    sls = list(sls)
    return sls, shulie_flag


def sl_solutuons_subs(solutions, target):
    answer = sl_search_multiple(solutions.sympify(), target)
    target_values = []
    if answer:
        for ans in answer:
            keys = list(ans.keys())
            if len(keys) == 1 and str(keys[0]) == str("n"):
                if ans[keys[0]].is_Integer:
                    new_value = target
                    new_value = new_value.subs(ans)
                    new_value = new_value.expand().simplify()
                    new_value = MathematicaSimplify().solver(BasePoly(new_value)).output[0].sympify()
                    target_values.append(new_value)
            else:
                new_value = target
                new_value = new_value.subs(ans)
                new_value = new_value.expand().simplify()
                # new_value = mathematicaSimplify().solver(BasePoly(new_value)).output[0].sympify()
                if (new_value - target).expand().simplify() != 0:
                    target_values.append(new_value)
        target_values = set(target_values)
        target_values = list(target_values)
    else:
        target_values.append(target)
    return target_values


def sl_solve_an(seqs, seq):
    try:
        solutions = MathematicaSolve().solver(BaseIneqs(seqs), BaseVariable(seq)).output[0].sympify()
        assert solutions
        answers = sl_search_multiple(solutions, seq)
        seq_values = []
        for ans in answers:
            seq_value = seq
            seq_value = seq_value.subs(ans)
            seq_values.append(seq_value)
    except Exception:
        solutions = {}
        assert len(seqs) == 1
        answers = solve(seqs[0][0] - seqs[0][1], seq)
        solutions[seq] = answers
        assert solutions
        answers = sl_search_multiple(solutions, seq)
        seq_values = []
        for ans in answers:
            seq_value = seq
            seq_value = seq_value.subs(ans)
            seq_values.append(seq_value)
    seq_values = set(seq_values)
    seq_values = list(seq_values)
    return seq_values


def sl_ditui_or_not_ditui(seqs):
    ditui_eqs = []
    nditui_eqs = []
    for eq in seqs:
        if len(eq) == 2:
            expr = sympify(eq[0]) - sympify(eq[1])
        else:
            expr = sympify(eq[0]) - sympify(eq[2])
        p = r'[a-zA-Z]_\(.*?\)'
        m = re.findall(p, str(expr))
        judge = []
        if m:
            if len(m) == 1:
                answers = solve(expr, sympify(m[0]))
                assert len(answers) == 1
                answer = answers[0]
                symbols = answer.free_symbols
                if sympify('n') not in symbols:
                    judge.append(0)
                else:
                    judge.append(1)
            else:
                for item in m:
                    new_item = simplify(item)
                    index = new_item.args[0]
                    symbols = index.free_symbols
                    if not symbols:
                        judge.append(0)
                    else:
                        judge.append(1)
        sum_judge = sum(judge)
        if sum_judge > 0:
            ditui_eqs.append(eq)
        else:
            nditui_eqs.append(eq)
    return ditui_eqs, nditui_eqs


def sl_belong_nbelong(seqs):
    belongs = []
    new_seqs = []
    for eq in seqs:
        if isinstance(eq, BaseBelong):
            belongs.append(eq)
        else:
            new_seqs.append(eq)
    return belongs, new_seqs


def sl_expr_to_an_value(expr, aname, shulie_name, cn_value, symbol):
    from mathsolver.functions.shulie.ShuLie_Transform import ShuLieSnExprNormalization
    expr = ShuLieSnExprNormalization().solver(BasePoly(expr), aname).output[0].sympify()
    expr_xiangs = sl_xiangs(expr)
    for expr_xiang in expr_xiangs:
        new_item = sympify(expr_xiang)
        new_item_index = new_item.args[0]
        if str(shulie_name) == str(aname):
            new_item_value = cn_value.subs({symbol: new_item_index})
            expr = expr.subs({new_item: new_item_value})
        else:
            if new_item_index == 1:
                new_item_value = cn_value.subs({symbol: new_item_index})
            else:
                new_item_value = cn_value.subs({symbol: new_item_index}) - cn_value.subs({symbol: new_item_index - 1})
            new_item_value = ShuLieSnExprNormalization().solver(BasePoly(new_item_value), aname).output[0].sympify()
            expr = expr.subs({new_item: new_item_value})
    expr = expr.expand().simplify()
    return expr


def sl_get_other_shulie_eqs(shulie, seqs):
    shulie_aname = shulie.aName
    shulie_sname = shulie.sName
    shulie_eqs = []
    for eq in seqs:
        flag = []
        if len(eq) == 2:
            expr = eq[0] - eq[1]
        else:
            expr = eq[0] - eq[2]
        expr_xiangs_names = sl_shulie_name(expr)
        for expr_xiangs_name in expr_xiangs_names:
            if str(expr_xiangs_name) == str(shulie_aname) or str(expr_xiangs_name) == str(shulie_sname):
                flag.append(0)
            else:
                flag.append(1)
        if sum(flag) == 0:
            shulie_eqs.append(eq)
    return shulie_eqs


def sl_get_target_shulie_eqs(shulie, seqs):
    shulie_aname = shulie.aName
    shulie_sname = shulie.sName
    shulie_eqs = []
    for eq in seqs:
        flag = []
        if len(eq) == 2:
            expr = eq[0] - eq[1]
        else:
            expr = eq[0] - eq[2]
        expr_xiangs_names = sl_shulie_name(expr)
        for expr_xiangs_name in expr_xiangs_names:
            if str(expr_xiangs_name) == str(shulie_aname) or str(expr_xiangs_name) == str(shulie_sname):
                flag.append(1)
            else:
                flag.append(0)
        if sum(flag) > 0:
            shulie_eqs.append(eq)
    return shulie_eqs


def sl_name_update(exprs, sl):
    """
    更新数列类的类名
    :param exprs:
    :param sl:
    :return: ShuLie
    """
    try:
        return sl
    except Exception:
        for key, value in exprs.items():
            if isinstance(value, (BaseBelong, BaseBelongs)):
                pass
            elif isinstance(value, BaseSeqInfPoly):
                p = r'[A-Z]_'
                m = re.findall(p, str(value))
                m = list(set(m))
                if len(m) == 1:
                    s_name = m[0]
                    sl.sName = s_name
                    a_name = sl.aName
                    name = a_name + s_name
                    sl.name = name
                    return sl
            elif isinstance(value, BaseSeqEqInf):
                left, right = value.value
                p1 = r'[A-Z]_'
                m1 = re.findall(p1, left)
                m1 = list(set(m1))
                p2 = r'[A-Z]_'
                m2 = re.findall(p2, right)
                m2 = list(set(m2))
                m = m1 + m2
                if len(m) == 1:
                    s_name = m[0]
                    sl.sName = s_name
                    a_name = sl.aName
                    name = a_name + s_name
                    sl.name = name
                    return sl
            else:
                value = value.sympify()
                if isinstance(value, list):
                    try:
                        if len(value) == 2:
                            expr = value[0] - value[1]
                        else:
                            expr = value[0] - value[2]
                        p = r'[A-Z]_'
                        m = re.findall(p, str(expr))
                        m = list(set(m))
                        if len(m) == 1:
                            s_name = m[0]
                            sl.sName = s_name
                            a_name = sl.aName
                            name = a_name + s_name
                            sl.name = name
                            return sl
                    except Exception:
                        for eq in value:
                            if len(eq) == 2:
                                expr = eq[0] - eq[1]
                            else:
                                expr = eq[0] - eq[2]
                            p = r'[A-Z]_'
                            m = re.findall(p, str(expr))
                            m = list(set(m))
                            if len(m) == 1:
                                s_name = m[0]
                                sl.sName = s_name
                                a_name = sl.aName
                                name = a_name + s_name
                                sl.name = name
                                return sl
                else:
                    p = r'[A-Z]_'
                    m = re.findall(p, str(value))
                    m = list(set(m))
                    if len(m) == 1:
                        s_name = m[0]
                        sl.sName = s_name
                        a_name = sl.aName
                        name = a_name + s_name
                        sl.name = name
                        return sl
        s_name = str("S_")
        sl.sName = s_name
        a_name = sl.aName
        name = a_name + s_name
        sl.name = name
        return sl


if __name__ == '__main__':
    pass
